#include "Environment.h"
#include "Player.h"
#include "Blocks.h"

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

using namespace std;

extern Player player;

void flipit(vector<vector<Block*> > &b) {
    vector<vector<Block*> > btemp;
    for (int i = b.size()-1; i >= 0; --i) {
        btemp.push_back(b[i]);
    }

    b.clear();
    b = btemp;
}

Environment::Environment(void) :
sX(80), sY(80) {
    char buf[90];

    FILE *fin = fopen("data/map", "r");

    vector<Block*> row;

    for (int r = sY-1; r >= 0; --r) {
        fgets(buf, 90, fin);
    for (int c = 0; c < sX; ++c) {
        if (buf[c] == '0')
            row.push_back(new DirtBlock(c, r));
        else if (buf[c] == '#')
            row.push_back(new StoneBlock(c, r));
        else if (buf[c] == 'X')
            row.push_back(new BedrockBlock(c, r));
        else
            row.push_back(new AirBlock(c, r));
    }
    world.push_back(row);
    row.clear();
    }

    flipit(world);
    fclose(fin);
}

void Environment::draw(float drawScale, ofPoint viewOffset) {
    ofSetColor(0x784800);

    for (int r = 0; r < this->sY; ++r) {
    	for (int c = 0; c < this->sX; ++c){
			if (objIsInView(c, r, drawScale, viewOffset))
            	this->wgrid()[r][c]->draw();
    	}
    }

	if (player.target.active) {
		int cX = player.target.block.x;
		int cY = player.target.block.y;
        ofEnableAlphaBlending();
        ofSetColor(255,255,255, this->wgrid()[cY][cX]->damageValue()*255);
        ofRect(cX, cY, 1, 1);

        ofSetColor(0, 0, 0, 255-255.0*this->wgrid()[cY][cX]->damageValue());
        ofNoFill();
        ofRect(cX, cY, 1, 1);
        ofFill();
        ofDisableAlphaBlending();
    }

    for (list<Particle*>::iterator it = this->particles.begin(); it != this->particles.end(); ++it) {
        (*it)->draw();
    }

    ofSetColor(0xFFFFFF);
    glEnable(GL_TEXTURE_2D);
	for (list<ItemParticle*>::iterator it = this->itemParticles.begin(); it != this->itemParticles.end(); ++it) {
        (*it)->draw();
    }
    glDisable(GL_TEXTURE_2D);
}

void Environment::update() {
	if (player.target.block.x != player.target.block_old.x || player.target.block.y != player.target.block_old.y) {
		if (player.target.block_old.x != -1)
			world[player.target.block_old.y][player.target.block_old.x]->stopHitting();
    }
	// TODO: player.target.active?
	if ((player.target.block.x != -1) && player.isSwinging()) {
		if (world[player.target.block.y][player.target.block.x]
            ->destroy(player.currentItem)) {
                delete world[player.target.block.y][player.target.block.x];

                world[player.target.block.y][player.target.block.x] =
                    new AirBlock(player.target.block.x, player.target.block.y);

                //player.target.set(-1,-1);
        }
    }

    for (list<Particle*>::iterator it = particles.begin(); it != particles.end(); ++it) {
		if (*it)
			if ((*it)->update()) {
				delete *it;
				*it = NULL;
			}
    }

	particles.remove(NULL);

    for (list<ItemParticle*>::iterator it = itemParticles.begin(); it != itemParticles.end(); ++it) {
        if (*it) {
            if ((*it)->update()) {
                delete *it;
                *it = NULL;
                continue;
            }
            if (ofDistSquared(player.xPos(), player.yPos()+0.5f, (*it)->loc.x, (*it)->loc.y) < 2) {
                if (!(*it)->hasBeenKilled())
                    if (player.hasRoomInInventoryForItem(*it)) {
                        if ((*it)->kill(player.xPos() - (*it)->loc.x, player.yPos() - (*it)->loc.y))
                            player.pickUpItem(*it);
                    }
            }
        }
    }

    itemParticles.remove(NULL);
}

void Environment::placeBlock(int ID) {
	if(!player.target.active) {
		return;
	}
	int x = player.target.block.x - player.target.block.direction.x;
	int y = player.target.block.y - player.target.block.direction.y;
	if (blockByCoord(x, y).transparent && spaceNotOccupiedByPlayer(x, y)) {
		delete world[y][x];
		//TODO from inventory - parameter?
        world[y][x] = allocateBlockByID(x, y, ID);
	}
}

void Environment::placeBlockAtCoord(int x, int y, Block* newlyAllocatedBlock) {
    if (!newlyAllocatedBlock || !spaceNotOccupiedByPlayer(x, y)) return;
    world[y][x] = newlyAllocatedBlock;
}

void Environment::addItemParticle(ofPoint loc, ofPoint vel, int ID) {
}

Block& Environment::blockByCoord(int x, int y) {
    return *world[y][x];
}

vector<vector<Block*> >& Environment::wgrid() {
    return world;
}

Block* Environment::activeBlock() {
	// TODO: player.target.active?
	if (player.target.block.x >= 0)
		return world[player.target.block.y][player.target.block.x];
    else
        return NULL;
}

bool Environment::objIsInView(float x, float y, float drawScale, ofPoint viewOffset) {
	ofRectangle originalWindow(0, 0, ofGetWidth(), ofGetHeight());

	originalWindow.width /= drawScale;
	originalWindow.width /= drawScale;

	originalWindow.x += viewOffset.x;
	originalWindow.y += viewOffset.y;

	if ((x < originalWindow.x+originalWindow.width) ||
		(x > originalWindow.x) ||
		(y < originalWindow.y+originalWindow.height) ||
		(y > originalWindow.y))
			return true;

	return false;
}

bool Environment::spaceNotOccupiedByPlayer(int x, int y) {
    int pX = (int)player.xPos(),
        pY = (int)player.yPos();

    if (((pX == x) && (pY == y)) ||
        ((pX == x) && (pY+1 == y)))
        return false;

    return true;
}

Environment::~Environment(void) {
    for (int r = 0; r < world.size();    ++r)
    for (int c = 0; c < world[r].size(); ++c) {
        delete world[r][c];
    }
}
